import os
import tempfile
import shutil
import unittest
from unittest.mock import patch, MagicMock
import sys
from multiprocessing import Manager
# 导入主程序模块
from multi_process_rm_dup import md5, process_file, rm_dup

class TestDuplicateRemover(unittest.TestCase):
    def setUp(self):
        # 创建临时目录作为测试环境
        self.test_dir = tempfile.mkdtemp()
        self.file_contents = {
            "file1.txt": "This is a test file",
            "file2.txt": "This is a test file",  # 与file1重复
            "file3.txt": "Different content",
            "file4.txt": "Another unique file",
            "file5.txt": "This is a test file",  # 与file1重复
            "image1.jpg": "Fake image data",
            "image2.jpg": "Fake image data",    # 与image1重复
            "ignore.me": "This should be ignored"
        }
        
        # 创建测试文件
        for filename, content in self.file_contents.items():
            file_path = os.path.join(self.test_dir, filename)
            with open(file_path, 'w') as f:
                f.write(content)
        
        # 记录初始文件列表用于验证
        self.initial_files = set(os.listdir(self.test_dir))
    
    def tearDown(self):
        # 清理临时目录
        shutil.rmtree(self.test_dir)
    
    def test_md5_calculation(self):
        """测试MD5哈希计算功能"""
        file1 = os.path.join(self.test_dir, "file1.txt")
        file2 = os.path.join(self.test_dir, "file2.txt")
        
        # 重复文件应该有相同的MD5
        self.assertEqual(md5(file1), md5(file2))
        
        # 不同文件应该有不同的MD5
        file3 = os.path.join(self.test_dir, "file3.txt")
        self.assertNotEqual(md5(file1), md5(file3))
    
    def test_process_file(self):
        """测试文件处理函数"""
        file1 = os.path.join(self.test_dir, "file1.txt")
        # 使用Manager创建计数器，与主程序保持一致
        manager = Manager()
        counter = manager.Value('i', 0)
        
        result = process_file(file1, counter)
        
        self.assertIsNotNone(result)
        self.assertIsInstance(result, tuple)
        self.assertEqual(len(result), 2)
        
        # 验证计数器已增加
        self.assertEqual(counter.value, 1)
        
        # 验证结果包含哈希+大小和文件路径
        file_hash = md5(file1)
        file_size = os.path.getsize(file1)
        expected_comb = f"{file_hash}{file_size}"
        self.assertEqual(result[0], expected_comb)
        self.assertEqual(result[1], file1)
        
        manager.shutdown()
    
    @patch('builtins.input', return_value='no')
    def test_rm_dup_detection(self, mock_input):
        """测试重复文件检测（不实际删除文件）"""
        # 保存当前目录以便恢复
        current_dir = os.getcwd()
        
        try:
            # 切换到测试目录执行操作
            os.chdir(self.test_dir)
            
            # 捕获删除操作和输出
            with patch('os.remove') as mock_remove, \
                 patch('sys.stdout', new_callable=MagicMock) as mock_stdout:
                
                rm_dup(self.test_dir, "")
                
                # 验证没有执行删除操作
                mock_remove.assert_not_called()
                
                # 验证输出内容
                output = mock_stdout.write.call_args_list
                output_str = ''.join([call[0][0] for call in output])
                
                self.assertIn("Found 8 files to process", output_str)
                self.assertIn("file2.txt", output_str)
                self.assertIn("file5.txt", output_str)
                self.assertIn("image2.jpg", output_str)
                self.assertIn("Operation cancelled by user. Exiting...", output_str)
                
                # 验证没有文件被删除
                current_files = set(os.listdir(self.test_dir))
                self.assertEqual(current_files, self.initial_files)
        finally:
            # 恢复工作目录
            os.chdir(current_dir)
    
    @patch('builtins.input', return_value='yes')
    def test_rm_dup_deletion(self, mock_input):
        """测试重复文件删除功能"""
        rm_dup(self.test_dir, "")
        
        remaining_files = os.listdir(self.test_dir)
        # 原8个，删除3个重复文件，剩余5个
        self.assertEqual(len(remaining_files), 5)
        
        # 验证保留的是第一个文件而非其他重复文件
        self.assertIn("file1.txt", remaining_files)  # 保留第一个文本文件
        self.assertIn("file3.txt", remaining_files)
        self.assertIn("file4.txt", remaining_files)
        self.assertIn("image1.jpg", remaining_files)  # 保留第一个图片文件
        self.assertIn("ignore.me", remaining_files)
        
        # 验证重复文件已被删除
        self.assertNotIn("file2.txt", remaining_files)
        self.assertNotIn("file5.txt", remaining_files)
        self.assertNotIn("image2.jpg", remaining_files)
    
    @patch('builtins.input', return_value='yes')
    def test_ignore_extensions(self, mock_input):
        """测试忽略指定扩展名的文件"""
        # 忽略 .me 扩展名的文件
        rm_dup(self.test_dir, ".me")
        
        remaining_files = os.listdir(self.test_dir)
        # 确认ignore.me文件被保留，其他重复文件被正确处理
        self.assertIn("ignore.me", remaining_files)
        self.assertEqual(len(remaining_files), 5)

if __name__ == '__main__':
    unittest.main()
